// Package main implements the Socket-Chat WebSocket-based chat server
package main

import (
	"encoding/json"
	"fmt"
	"html"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"sync"

	"github.com/gorilla/websocket"
)

// Client represents a chat client with connection details
type Client struct {
	conn *websocket.Conn
	name string
	room string
}

// ChatRoom represents a chat room with clients and message history
type ChatRoom struct {
	password       string
	clients        map[*Client]bool
	messageHistory []string
	mu             sync.Mutex
}

// Global rooms map and mutex for thread-safe access
var rooms = make(map[string]*ChatRoom)
var roomsMutex sync.Mutex

// Config struct to hold configuration values
type Config struct {
	ProjectName string `json:"projectName"`
	Port        string `json:"port"`
}

func main() {
	// Read configuration from config.json
	configData, err := ioutil.ReadFile("config.json")
	if err != nil {
		log.Fatal("无法读取配置文件:", err)
	}

	var config Config
	if err := json.Unmarshal(configData, &config); err != nil {
		log.Fatal("无法解析配置文件:", err)
	}
	// Serve client HTML file with configuration
	http.HandleFunc("/"+config.ProjectName+"/", func(w http.ResponseWriter, r *http.Request) {

		// Read the client.html file
		htmlContent, err := ioutil.ReadFile("client.html")
		if err != nil {
			http.Error(w, "无法读取客户端文件", http.StatusInternalServerError)
			return
		}

		// Convert to string and replace placeholders with actual values
		updatedContent := strings.ReplaceAll(string(htmlContent), "Socket-Chat", config.ProjectName)

		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		w.Write([]byte(updatedContent))
	})

	// Upgrade HTTP connection to WebSocket
	var upgrader = websocket.Upgrader{
		CheckOrigin: func(r *http.Request) bool { return true },
	}

	// Handle WebSocket connections with project name prefix
	http.HandleFunc("/"+config.ProjectName+"/ws", func(w http.ResponseWriter, r *http.Request) {
		log.Println("收到带项目名称的WebSocket连接请求")
		conn, err := upgrader.Upgrade(w, r, nil)
		if err != nil {
			log.Println("WebSocket升级失败:", err)
			return
		}
		log.Println("WebSocket连接升级成功")
		client := &Client{conn: conn}
		go handleClient(client)
	})

	// Serve configuration
	http.HandleFunc("/"+config.ProjectName+"/config", func(w http.ResponseWriter, r *http.Request) {

		configData, err := ioutil.ReadFile("config.json")
		if err != nil {
			http.Error(w, "无法读取配置文件", http.StatusInternalServerError)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		w.Write(configData)
	})

	// Handle file upload
	http.HandleFunc("/"+config.ProjectName+"/upload", func(w http.ResponseWriter, r *http.Request) {
		// Check if it's a POST request
		if r.Method != "POST" {
			http.Error(w, "只支持POST方法", http.StatusMethodNotAllowed)
			return
		}

		// Parse multipart form with max memory of 32MB
		err := r.ParseMultipartForm(32 << 20)
		if err != nil {
			http.Error(w, "无法解析表单数据", http.StatusBadRequest)
			return
		}

		// Get file from form
		file, handler, err := r.FormFile("file")
		if err != nil {
			http.Error(w, "无法获取文件", http.StatusBadRequest)
			return
		}
		defer file.Close()

		// Create uploads directory if it doesn't exist
		os.MkdirAll("files", os.ModePerm)

		// Create destination file
		dest, err := os.Create("files/" + handler.Filename)
		if err != nil {
			http.Error(w, "无法创建文件", http.StatusInternalServerError)
			return
		}
		defer dest.Close()

		// Copy uploaded file to destination
		_, err = io.Copy(dest, file)
		if err != nil {
			http.Error(w, "无法保存文件", http.StatusInternalServerError)
			return
		}

		// Return success response
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("文件上传成功"))
	})

	// Handle file download
	http.HandleFunc("/"+config.ProjectName+"/download/", func(w http.ResponseWriter, r *http.Request) {
		// Extract filename from URL path
		filename := strings.TrimPrefix(r.URL.Path, "/"+config.ProjectName+"/download/")
		if filename == "" {
			http.Error(w, "文件名不能为空", http.StatusBadRequest)
			return
		}

		// Construct file path
		filePath := "files/" + filename

		// Check if file exists
		if _, err := os.Stat(filePath); os.IsNotExist(err) {
			http.Error(w, "文件不存在", http.StatusNotFound)
			return
		}

		// Set content disposition header for download
		w.Header().Set("Content-Disposition", "attachment; filename="+filename)
		w.Header().Set("Content-Type", "application/octet-stream")

		// Serve file
		http.ServeFile(w, r, filePath)
	})

	log.Printf("%s 聊天服务器启动于 :%s\n", config.ProjectName, config.Port)
	if err := http.ListenAndServe(":"+config.Port+"", nil); err != nil {
		log.Fatal("HTTP服务器启动失败:", err)
	}
}

// handleClient manages the lifecycle of a chat client connection
func handleClient(client *Client) {
	defer func() {
		client.conn.Close()
		roomsMutex.Lock()
		if room, exists := rooms[client.room]; exists {
			room.mu.Lock()
			delete(room.clients, client)
			room.mu.Unlock()
			broadcastToRoom(room, fmt.Sprintf("%s left the chat room", client.name))
		}
		roomsMutex.Unlock()
	}()

	log.Printf("等待客户端 %p 认证信息", client)
	// Read authentication information from client (name, room, password)
	_, authBuffer, err := client.conn.ReadMessage()
	if err != nil {
		log.Printf("读取客户端 %p 认证信息错误: %v", client, err)
		return
	}
	authInfo := string(authBuffer)
	log.Printf("收到客户端 %p 认证信息: %s", client, authInfo)
	parts := strings.Split(authInfo, "\n")
	if len(parts) < 2 {
		log.Printf("客户端 %p 认证格式无效", client)
		client.conn.WriteMessage(websocket.TextMessage, []byte("认证格式无效，请刷新后重试"))
		return
	}
	client.name = strings.TrimSpace(parts[0])
	roomName := strings.TrimSpace(parts[1])
	password := ""
	if len(parts) > 2 {
		password = strings.TrimSpace(parts[2])
	}
	log.Printf("客户端 %p 信息 - 用户名: %s, 房间名: %s, 密码: %s", client, client.name, roomName, password)

	// Handle room creation or joining
	roomsMutex.Lock()
	log.Printf("尝试加入房间: %s", roomName)
	room, exists := rooms[roomName]
	if !exists {
		log.Printf("创建新房间: %s", roomName)
		// Create new room
		room = &ChatRoom{
			password:       password,
			clients:        make(map[*Client]bool),
			messageHistory: []string{},
		}
		rooms[roomName] = room
	} else {
		log.Printf("加入现有房间: %s", roomName)
		// 加入现有房间并检查密码
		if password != room.password {
			log.Printf("密码错误，客户端 %s 无法加入房间 %s", client.name, roomName)
			client.conn.WriteMessage(websocket.TextMessage, []byte("密码错误，连接已关闭"))
			roomsMutex.Unlock()
			return
		}
	}
	client.room = roomName

	// 将客户端添加到房间并发送历史消息
	log.Printf("将客户端 %s 添加到房间 %s", client.name, roomName)
	room.mu.Lock()
	room.clients[client] = true
	log.Printf("向客户端 %s 发送历史消息，共 %d 条", client.name, len(room.messageHistory))
	for _, msg := range room.messageHistory {
		if err := client.conn.WriteMessage(websocket.TextMessage, []byte(msg)); err != nil {
			log.Printf("向客户端 %s 发送历史消息失败: %v", client.name, err)
			// 从房间中移除客户端
			delete(room.clients, client)
			room.mu.Unlock()
			roomsMutex.Unlock()
			return
		}
	}
	room.mu.Unlock()
	roomsMutex.Unlock()

	log.Printf("向房间 %s 广播客户端 %s 加入消息", roomName, client.name)
	broadcastToRoom(room, fmt.Sprintf("%s 加入了聊天室", client.name))
	log.Printf("向客户端 %s 发送连接成功消息", client.name)
	if err := client.conn.WriteMessage(websocket.TextMessage, []byte("连接成功，已加入聊天室")); err != nil {
		log.Printf("向客户端 %s 发送连接成功消息失败: %v", client.name, err)
		// 从房间中移除客户端
		roomsMutex.Lock()
		if room, exists := rooms[client.room]; exists {
			room.mu.Lock()
			delete(room.clients, client)
			room.mu.Unlock()
		}
		roomsMutex.Unlock()
		return
	}

	// Handle incoming messages
	for {
		_, messageBuffer, err := client.conn.ReadMessage()
		if err != nil {
			log.Printf("客户端 %s 连接错误: %v", client.name, err)
			break
		}
		message := string(messageBuffer)
		log.Printf("收到来自 %s 的消息: %s", client.name, message)
		formattedMsg := fmt.Sprintf("%s: %s", client.name, html.EscapeString(message))

		// 先释放roomsMutex锁，再获取房间锁以避免死锁
		roomsMutex.Lock()
		room, exists := rooms[client.room]
		roomsMutex.Unlock()

		if exists {
			room.mu.Lock()
			room.messageHistory = append(room.messageHistory, formattedMsg)
			room.mu.Unlock()

			log.Printf("向房间 %s 广播消息: %s", client.room, formattedMsg)
			broadcastToRoom(room, formattedMsg)
		}
	}
}

// broadcastToRoom sends a message to all clients in a chat room
func broadcastToRoom(room *ChatRoom, message string) {
	room.mu.Lock()
	defer room.mu.Unlock()
	log.Printf("向房间 %s 中的 %d 个客户端广播", room.password, len(room.clients))

	// Create a copy of the clients map to iterate over
	clientsCopy := make(map[*Client]bool)
	for client := range room.clients {
		clientsCopy[client] = true
	}

	for client := range clientsCopy {
		log.Printf("向客户端 %s (%p) 发送消息", client.name, client)
		if err := client.conn.WriteMessage(websocket.TextMessage, []byte(message)); err != nil {
			log.Printf("向客户端 %s (%p) 发送消息失败: %v", client.name, client, err)
			client.conn.Close()
			// Remove the client from the original map
			delete(room.clients, client)
		} else {
			log.Printf("成功向客户端 %s (%p) 发送消息", client.name, client)
		}
	}
}
